# !/usr/bin/env python
# -*- coding: utf-8 -*-
import datetime
import functools
import json
import logging
import re
import urllib
import uuid
from urllib.parse import quote

import statsd
import tornado
from jinja2 import Environment, FileSystemLoader, TemplateNotFound
from sqlalchemy import func, or_
from tornado.httpclient import AsyncHTTPClient
from tornado.web import RequestHandler
from wechatpy import WeChatOAuth

from base.connection import get_db_session, mongodb_util, redis_util
from base.model import Object
from base.session import Session
from common.definition import AgencyType, ret_msg
from conf import config
from conf.config import (
    DEFAULT_AGENCY_UID,
    HOST_NAME,
    MP_APP_ID,
    MP_APP_SECRET,
    STATSD_PORT,
    STATSD_SERVER,
    VERSION_CODE,
    DOMAIN,
)
from handlers.filters import get_item, json_loads
from helper.device import get_device
from helper.exceptions import RequestEnd
from helper.mp_helper import fetch_simple_access_token, fetch_simple_jsapi_ticket
from helper.user import UserHelper, get_user
from util.str_util import replace_emoji

logger = logging.getLogger("wechat")
stats = statsd.StatsClient(STATSD_SERVER, STATSD_PORT)


class TemplateRendering(object):
    """
    A simple class to hold methods for rendering templates.
    """

    def render_template(self, template_name, **kwargs):
        template_dirs = []
        if self.settings.get("template_path", ""):
            template_dirs.append(self.settings["template_path"])
        env = Environment(loader=FileSystemLoader(template_dirs))
        # 　添加自定义过滤器
        env.filters["json_loads"] = json_loads
        env.filters["get_item"] = get_item

        try:
            template = env.get_template(template_name)
        except TemplateNotFound:
            raise TemplateNotFound(template_name)
        content = template.render(kwargs)
        return content


class BaseHandler(RequestHandler, TemplateRendering):
    def __init__(self, application, request, **kwargs):
        super(BaseHandler, self).__init__(application, request, **kwargs)

    def data_received(self, chunk):
        pass

    def prepare(self):
        stats.incr("gradevin_wechat")

    def write_error(self, status_code, **kwargs):
        """
        重写错误提示页面
        :param status_code:
        :param kwargs:
        :return:
        """
        context = dict(
            page_title="维护中",
            title="维护中",
            description="系统升级，请稍后再试！",
        )
        return self.render_html("error.html", **context)

    @classmethod
    async def async_request(
        cls, url, data=None, headers=None, method="GET", timeout=30
    ):
        """
        异步web请求
        :param url:
        :param data: 请求参数 dict
        :param headers: 请求头 dict
        :param method: 请求方式 GET/POST
        :param timeout: 请求超时时间，单位秒
        :return: 请求结果字符串
        """
        if data is None:
            data = {}
        request_timeout = timeout  # 请求超时时间
        _params = dict(
            allow_nonstandard_methods=method,
            method=method,
            request_timeout=request_timeout,
        )
        if headers:
            _params["headers"] = headers
        params = ""
        for k, v in data.items():
            params += k + "=%s&" % quote(str(v))
        if params:
            logger.debug(params)
            params = params[0:-1]
            if "?" in url:
                url += "&" + params
            else:
                url = url + "?" + params
        response = await AsyncHTTPClient().fetch(url, **_params, validate_cert=False)
        result = response.body.decode()
        return result

    def is_in_wechat(self):
        agent = self.request.headers.get("User-Agent", None)
        is_wechat = False
        if agent and "MicroMessenger" in agent:
            is_wechat = True
        return is_wechat

    @staticmethod
    def is_membership(open_id):
        # 停用之前的会员价
        return False

        # 直接在屏端购买的为会员价
        if open_id is None:
            return False

        if re.match(r"^\d+$", open_id):
            return True

        # 合伙人送VIP身份
        class UserAgency(object):
            pass

        class Agency(object):
            pass

        session = get_db_session(
            [
                {"db_table": "ebt_user_agency", "model": UserAgency},
                {"db_table": "ebt_agency", "model": Agency},
            ]
        )
        user_agency = (
            session.query(UserAgency).filter_by(ebf_ua_bind_user_id=open_id).first()
        )
        if user_agency:
            agency = (
                session.query(Agency)
                .filter_by(ebf_agency_id=user_agency.ebf_agency_id)
                .filter(Agency.ebf_agency_type >= 1)
                .first()
            )
            if agency:
                return True

        # 酒柜主人就是会员VIP
        db = mongodb_util.db
        device = db.devices.find_one({"device_type": 15, "master": open_id})
        if device:
            return True
        else:
            return False

    @staticmethod
    def is_member(open_id):
        # 直接在屏端购买的为会员价
        if open_id is None:
            return False

        if re.match(r"^\d+$", open_id):
            return True

        db = mongodb_util.db
        device = db.devices.find_one({"device_type": 15, "master": open_id})
        if device:
            return True
        else:
            return False

    @staticmethod
    def is_vip(open_id):
        """是否为VIP"""

        class User(object):
            pass

        session = get_db_session(
            [
                {"db_table": "ebt_user", "model": User},
            ]
        )

        user = session.query(User).filter_by(ebf_user_id=open_id).first()
        level = user.ebf_user_level
        if level == 10:
            session.close()
            return True
        else:
            session.close()
            return False

    @staticmethod
    def is_partner(open_id):
        """是否为合伙人"""

        class User(object):
            pass

        class Agency(object):
            pass

        class UserAgency(object):
            pass

        session = get_db_session(
            [
                {"db_table": "ebt_user", "model": User},
                {"db_table": "ebt_agency", "model": Agency},
                {"db_table": "ebt_user_agency", "model": UserAgency},
            ]
        )

        ua = session.query(UserAgency).filter_by(ebf_ua_bind_user_id=open_id).first()
        if ua:
            agency_id = ua.ebf_agency_id
            agency = session.query(Agency).filter_by(ebf_agency_id=agency_id).first()
            if agency:
                is_active = agency.ebf_agency_is_active
                agency_type = agency.ebf_agency_type
                if is_active and agency_type > 0:
                    session.close()
                    return True
        session.close()
        return False

    @staticmethod
    def is_eligible_for_member_price(open_id: str) -> bool:
        """是否享受合伙人内供价格"""

        class User(object):
            pass

        class Agency(object):
            pass

        class UserAgency(object):
            pass

        session = get_db_session(
            [
                {"db_table": "ebt_user", "model": User},
                {"db_table": "ebt_agency", "model": Agency},
                {"db_table": "ebt_user_agency", "model": UserAgency},
            ]
        )

        ua = session.query(UserAgency).filter_by(ebf_ua_bind_user_id=open_id).first()
        if ua:
            agency_id = ua.ebf_agency_id
            agency = session.query(Agency).filter_by(ebf_agency_id=agency_id).first()
            if agency:
                is_active = agency.ebf_agency_is_active
                agency_type = agency.ebf_agency_type
                if is_active and agency_type > 0:
                    session.close()
                    return True
        session.close()
        return False

    @staticmethod
    def is_eligible_for_pin_price(open_id: str) -> bool:
        """是否享受门店经销商价格"""

        class User(object):
            pass

        class Agency(object):
            pass

        class UserAgency(object):
            pass

        session = get_db_session(
            [
                {"db_table": "ebt_user", "model": User},
                {"db_table": "ebt_agency", "model": Agency},
                {"db_table": "ebt_user_agency", "model": UserAgency},
            ]
        )

        ua = session.query(UserAgency).filter_by(ebf_ua_bind_user_id=open_id).first()
        if ua:
            agency_id = ua.ebf_agency_id
            agency = session.query(Agency).filter_by(ebf_agency_id=agency_id).first()
            if agency:
                is_active = agency.ebf_agency_is_active
                agency_type = agency.ebf_agency_type
                agency_sub_type = agency.ebf_agency_sub_type
                if (
                    is_active
                    and agency_type == AgencyType.STORE
                    and agency_sub_type > 0
                ):
                    session.close()
                    return True
        session.close()
        return False

    @staticmethod
    def get_device_master_open_id(device_open_id: str) -> str:
        db = mongodb_util.db
        device = db.devices.find_one({"device_type": 15, "_id": device_open_id})
        if device:
            return device["master"]
        else:
            return None

    def get_wechat_subscribe(self) -> str:
        url = (
            "https://open.weixin.qq.com/connect/oauth2/authorize?appid={appid}&redirect_uri={url}"
            "&response_type=code&scope=snsapi_userinfo&state=mall#wechat_redirect"
        )
        host = self.request.host
        request_path = self.request.path
        url_params = self.request.query
        protocol = self.request.protocol
        full_url = protocol + "://" + host + request_path + "?" + url_params
        url = url.format(url=urllib.request.quote(full_url), appid=MP_APP_ID)
        logger.info("url=%s" % url)
        return url

    @staticmethod
    def get_access_token(reset=False) -> str:
        """
        获取微信的access_token
        :return:
        """
        access_token = fetch_simple_access_token(reset=reset)
        logger.info("access_token=%s" % access_token)
        return access_token

    @staticmethod
    def get_jsapi_ticket():
        """
        {
        "errcode":0,
        "errmsg":"ok",
        "ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA",
        "expires_in":7200
        }
        :return:
        """
        access_token = fetch_simple_access_token()
        jsapi_ticket = fetch_simple_jsapi_ticket(access_token=access_token)
        logger.info("jsapi_ticket=%s" % jsapi_ticket)
        return jsapi_ticket

    @classmethod
    async def get_oauth_access_token(cls, code):
        """
        { "access_token":"ACCESS_TOKEN",
         "expires_in":7200,
         "refresh_token":"REFRESH_TOKEN",
         "openid":"OPENID",
         "scope":"SCOPE" }
        :param code:
        :return:
        """
        url = (
            "https://api.weixin.qq.com/sns/oauth2/access_token?appid={appid}&secret={secret}&code={code}&"
            "grant_type=authorization_code"
        )
        url = url.format(appid=MP_APP_ID, secret=MP_APP_SECRET, code=code)
        response = await cls.async_request(url)
        logger.debug(response)
        json_rsp = json.loads(response)
        return json_rsp["openid"]

    async def get_open_id(self):
        device_id = self.get_argument("device_id", None)
        if device_id:
            logger.info("device_id=%s" % device_id)
            return device_id

        user_json = self.get_secure_cookie("sid3")
        open_id = None
        if config.IS_LOCAL:
            # open_id = "o7qyl0vKx54jB05FjmxNw76SJAPE"
            open_id = "o7qyl0i6R30kbFS7KPFVGwYb05pY"
        else:
            # 生产环境登录
            host = self.request.host
            request_path = self.request.path
            logger.info("get_open_id: host=%s, path=%s" % (host, request_path))
            if host.startswith("www"):
                session_id = self.get_secure_cookie("session_id")
                if session_id:
                    self.session = Session(self)
                    user_info = self.session.data
                    if user_info:
                        if user_info["unionid"]:
                            logger.info(
                                "unionid=%s, nickname=%s"
                                % (user_info["unionid"], user_info["nickname"])
                            )
                            return user_info["unionid"]
                        elif user_info["mobile"]:
                            logger.info("mobile=%s" % user_info["mobile"])
                            return user_info["mobile"]
                    else:
                        return None
                else:
                    return None
            if not user_json:
                code = self.get_argument("code", None)
                if not code:
                    self.redirect(self.get_wechat_subscribe())
                    raise RequestEnd
                # code提交两次的问题(将code放入redis中)
                # 通过code 获取用户信息
                redis = redis_util.client
                old_code = redis.get("style999_code_%s" % code)
                if not old_code:
                    open_id = await self.get_oauth_access_token(code)
                    if open_id:
                        redis.setex("style999_code_%s" % code, 4, open_id)
                else:
                    open_id = old_code.decode()
                # 检查用户是否存在，如果用户列表中没有时需要创建
                is_exists = redis.get("style999_user_%s" % open_id)
                if is_exists is None:
                    # 查询数据库
                    user = UserHelper.get_user(open_id)
                    if user is None:
                        # 创建用户
                        user_dict = dict(openid=open_id)
                        UserHelper.create_user(user_dict, DEFAULT_AGENCY_UID)
                    redis.set("style999_user_%s" % open_id, 1)

            else:
                user = tornado.escape.json_decode(user_json)
                open_id = user["openid"]

            if not open_id:
                self.finish(ret_msg(-1, error_message="openid为空"))
                raise RequestEnd

            # 写入cookie
            self.set_secure_cookie(
                "sid3",
                tornado.escape.json_encode(
                    dict(
                        openid=open_id,
                    )
                ),
            )
        logger.info("openid=%s" % open_id)
        return open_id

    @staticmethod
    def get_db_session(mapper_list):
        """
        获取sqlalchemy的session
        :param mapper_list: 例如：[{"db_table":"sdet_goods","model":Goods}]
        :return:
        """
        session = get_db_session(mapper_list)
        return session

    def get_current_user(self):
        """
        需要用户信息的事件加此装饰器
        :return:
        """

        openid = self.get_argument("openid", "")
        user = get_user(openid)
        device = get_device(openid)
        if user is not None:
            if device:
                current_user = dict(user, **device)
            else:
                current_user = dict(user)
            return Object(**current_user)
        else:
            return None

    def render_html(self, template_name, **kwargs):
        if self._finished:
            return
        kwargs.update(
            {
                "settings": self.settings,
                # 'STATIC_URL': self.settings.get('static_url_prefix', ''),
                "STATIC_URL": "",
                "version_code": VERSION_CODE,
                "request": self.request,
                # "static_path": settings.STATIC_PATH,
                "current_user": self.current_user,
                "xsrf_token": self.xsrf_token,
                "xsrf_form_html": self.xsrf_form_html,
            }
        )
        content = self.render_template(template_name, **kwargs)
        self.write(content)

    async def create_user(self, open_id, agency_uid, recommend_user=""):
        """
         {
        "subscribe": 1,
        "openid": "o6_bmjrPTlm6_2sgVt7hMZOPfL2M",
        "nickname": "Band",
        "sex": 1,
        "language": "zh_CN",
        "city": "广州",
        "province": "广东",
        "country": "中国",
        "headimgurl":  "http://wx.qlogo.cn/mmopen/g3MonUZtNHkdmzicI
        libx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4
         eMsv84eavHiaiceqxibJxCfHe/0",
           "subscribe_time": 1382694957,
           "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"
           "remark": "",
           "groupid": 0,
           "tagid_list":[128,2]
         }

         没关注的话只能获取如下信息

         {"subscribe":0,"openid":"o7qyl0iYIEldo1SxrDonwrIxzDpk","tagid_list":[]}

         :param open_id:
         :param agency_uid:
         :param recommend_user:
         :return:
        """
        url = "https://api.weixin.qq.com/cgi-bin/user/info"
        access_token = self.get_access_token()
        params = dict(
            access_token=access_token,
            openid=open_id,
            lang="zh_CN",
        )
        response = await self.async_request(url=url, data=params, timeout=30)
        logger.debug(response)
        user_dict = json.loads(response)

        user = UserHelper.create_user(user_dict, agency_uid, recommend_user)
        return user

    async def get_user_info(self, open_id):
        url = "https://api.weixin.qq.com/cgi-bin/user/info"
        access_token = self.get_access_token()
        logger.info("access_token=%s" % access_token)
        params = dict(
            access_token=access_token,
            openid=open_id,
            lang="zh_CN",
        )
        response = await self.async_request(url=url, data=params, timeout=30)
        logger.debug(response)
        user_info = json.loads(response)
        if user_info.get("errcode", 0) == 40001:
            # invalid credential, access_token is invalid or not latest
            logger.error(response)
            access_token = self.get_access_token(reset=True)
            params = dict(
                access_token=access_token,
                openid=open_id,
                lang="zh_CN",
            )
            response = await self.async_request(url=url, data=params, timeout=30)
            logger.debug(response)
            user_info = json.loads(response)
        return user_info

    async def update_nickname(self, open_id):
        class User(object):
            pass

        session = self.get_db_session(
            [
                {"db_table": "ebt_user", "model": User},
            ]
        )
        user = session.query(User).filter_by(ebf_user_id=open_id).first()
        if user:
            u_nickname = user.ebf_user_nickname
            if u_nickname == "未关注" or u_nickname == "" or u_nickname is None:
                url = "https://api.weixin.qq.com/cgi-bin/user/info"
                access_token = self.get_access_token()
                params = dict(
                    access_token=access_token,
                    openid=open_id,
                    lang="zh_CN",
                )
                response = await self.async_request(url=url, data=params, timeout=30)
                logger.debug(response)
                user_dict = json.loads(response)
                nickname = replace_emoji(user_dict.get("nickname", "未关注"))
                if nickname != "未关注":
                    user.ebf_user_nickname = nickname
                    session.commit()

    async def bind_user(self, open_id):
        """
        绑定用户的合伙人和推荐人
        :param open_id: 当前用户openid
        :return:
        """

        jstarcode = self.get_argument("jstarcode", None)
        if not jstarcode:
            jstarcode = self.get_argument("id", None)
            if not jstarcode:
                jstarcode = self.get_argument("ID", None)
                if not jstarcode:
                    jstarcode = self.get_argument("Id", None)

        owner_open_id = self.get_argument("oid", None)
        if not (owner_open_id or jstarcode):
            logger.debug(f"bind_user: no owner_open_id or jstarcode! open_id={open_id}")
            return

        # 非微信用户
        if re.match(r"^\d+$", open_id):
            return

        class User(object):
            pass

        class Agency(object):
            pass

        class UserAgency(object):
            pass

        class Device(object):
            pass

        session = get_db_session(
            [
                {"db_table": "ebt_user", "model": User},
                {"db_table": "ebt_agency", "model": Agency},
                {"db_table": "ebt_user_agency", "model": UserAgency},
                {"db_table": "sdet_device", "model": Device},
            ]
        )

        # 处理智慧导购屏扫码绑定
        if jstarcode:
            if int(jstarcode) > 1000000:
                # 按设备ID处理，获取门店ID
                device_id = jstarcode
                device = session.query(Device).filter_by(id=device_id).first()
                if device and device.id:
                    agency_id = device.agency_id
                    jstarcode = str(agency_id)  # 设备已绑定，后续按agency_id处理
                else:
                    if not device:
                        try:
                            # 新增设备
                            device = Device()
                            device.id = device_id
                            session.add(device)
                            session.commit()
                            logger.info(f"设备扫码：新增设备 device_id={device_id}")
                        except Exception as e:
                            logger.exception(e)

                    agency_uid = UserHelper.get_agency_uid(open_id)
                    if not agency_uid:
                        logger.error(f"设备扫码: 非合伙人扫码，不绑定: device_id={device_id}")
                        session.close()
                        return

                    agency = UserHelper.get_agency(agency_uid)
                    if agency.ebf_agency_type == AgencyType.STORE:
                        agency_id = agency.ebf_agency_id
                        jstarcode = str(agency_id)  # 后续按agency_id处理
                        # 更新设备表
                        device.agency_id = agency_id
                        session.commit()
                        logger.info(f"设备扫码: 绑定门店 device_id={device_id} agency_id={agency_id}")
                    else:
                        logger.error(f"设备扫码: 非门店扫码，不绑定: device_id={device_id}")
                        session.close()
                        return

            if re.match(r"^\d+$", jstarcode):
                # 合伙人ID
                agency_id = jstarcode
                ua = (
                    session.query(UserAgency).filter_by(ebf_agency_id=agency_id).first()
                )
                if ua:
                    owner_open_id = ua.ebf_ua_bind_user_id
            else:
                # 推荐人openid
                owner_open_id = jstarcode
        # =====================================================================
        # 取消自己推荐自己的限制 - 因为店主需要在自己的店扫码下单 20220915 => 限制自己推荐自己 - 导致推荐奖励问题 20221121
        if open_id == owner_open_id:
            logger.debug(f"bind_user: open_id == owner_open_id: open_id={open_id}")
            session.close()
            return

        owner_user = session.query(User).filter_by(ebf_user_id=owner_open_id).first()
        if not owner_user:
            session.close()
            return
        agency_uid = owner_user.ebf_user_agency_uid

        user = session.query(User).filter_by(ebf_user_id=open_id).first()
        if not user:
            # 用户不存在时创建用户
            await self.create_user(open_id, agency_uid, owner_open_id)
            logger.info(
                "bind_user： 新用户创建成功，open_id=%s,recommend_user=%s,agency_uid=%s"
                % (open_id, owner_open_id, agency_uid)
            )
            session.close()
            return

        # 实时更新用户推荐人（每次都更新）
        recommend_user = owner_open_id
        old_recommend_user = user.ebf_user_recommend_user
        if (
            recommend_user
            and old_recommend_user != recommend_user
        ):
            user.ebf_user_recommend_user = recommend_user
            session.commit()
            logger.info(
                "bind_user：更新推荐人，open_id=%s, recommend_user=%s, old_recommend_user=%s, agency_uid=%s"
                % (open_id, recommend_user, old_recommend_user, agency_uid)
            )

        # 如果是默认合伙人的用户还能重新绑定其它合伙人
        if not (
            user.ebf_user_agency_uid
            and user.ebf_user_agency_uid != config.DEFAULT_AGENCY_UID
        ):
            agency_uid = owner_user.ebf_user_agency_uid
            # 绑定合伙人
            user.ebf_user_agency_uid = agency_uid
            # 记录用户的推荐人
            user.ebf_user_recommend_user = owner_open_id
            session.commit()
            logger.info(
                "bind_user： 重新绑定合伙人，open_id=%s,recommend_user=%s,agency_uid=%s"
                % (open_id, owner_open_id, agency_uid)
            )
        session.close()


class ApiBaseHandler(BaseHandler):
    """api 基类"""

    def prepare(self):
        stats.incr("gradevin_wechat")
        # self.get_parameter("参数名") 可以接受json或者form表单或者查询字符串参数（无论post还是get请求）
        if self.request.headers.get("Content-Type", "").startswith("application/json"):
            self.json_args = json.loads(self.request.body.decode("utf-8"))
            self.get_parameter = self.json_args.get
        else:
            self.get_parameter = self.get_argument
        pass

    def set_default_headers(self):
        self.set_header("Content-Type", "application/json; charset=UTF-8")
        self.set_header("APISERVER", HOST_NAME)

    # 可排序字段
    sortable_fields = []

    def get_sort_field(self, sort):
        if not sort:
            return
        ret = list()
        sort_list = sort.split(",")

        for s in sort_list:
            s_arr = s.split(".")
            if len(s_arr) != 2:
                continue
            field = s_arr[0]
            sort_method = s_arr[1]
            if not (sort_method and sort_method.lower() in ["asc", "desc"]):
                continue
            if field not in self.sortable_fields:
                continue
            ret.append(
                (
                    field,
                    sort_method.lower(),
                )
            )
        if len(ret) == 0:
            return
        return ret

    def get_agency_uid(self, open_id):
        """
        查找当前用户有没绑定合伙人
        :param open_id:
        :return:
        """

        class UserAgency(object):
            pass

        class Agency(object):
            pass

        session = self.get_db_session(
            [
                {"db_table": "ebt_user_agency", "model": UserAgency},
                {"db_table": "ebt_agency", "model": Agency},
            ]
        )

        user_agency = (
            session.query(UserAgency).filter_by(ebf_ua_bind_user_id=open_id).first()
        )
        if not user_agency:
            self.finish(ret_msg(code=-1, error_message="当前openid未绑定合伙人"))
            raise RequestEnd

        agency = (
            session.query(Agency)
            .filter_by(ebf_agency_id=user_agency.ebf_agency_id)
            .first()
        )
        if not agency:
            self.finish(ret_msg(code=-1, error_message="合伙人不存在"))
            raise RequestEnd

        agency_uid = agency.ebf_agency_uid
        return agency_uid

    def get_agency_openid(self, agency_id):
        """
        查找合伙人绑定的open_id
        :param agency_id:
        :return:
        """

        class UserAgency(object):
            pass

        session = self.get_db_session(
            [
                {"db_table": "ebt_user_agency", "model": UserAgency},
            ]
        )

        user_agency = (
            session.query(UserAgency).filter_by(ebf_agency_id=agency_id).first()
        )
        if not user_agency:
            logger.error(f"当前合伙人账户未绑定微信: agency_id={agency_id} ")
            return None

        agency_openid = user_agency.ebf_ua_bind_user_id
        return agency_openid

    def write_error(self, status_code, **kwargs):
        """
        重写错误提示页面
        :param status_code:
        :param kwargs:
        :return:
        """

        return self.write(ret_msg(-1))

    def power_auth(self, open_id):
        """
        权限认证
        :param open_id:
        :return:
        """
        oid_list = [
            "o7qyl0i6R30kbFS7KPFVGwYb05pY",
            "o7qyl0kQ11-oF9RV6__m6t1JVpDk",
            "o7qyl0vKx54jB05FjmxNw76SJAPE",
        ]

        if not open_id:
            return False

        if open_id in oid_list:
            return True
        else:
            return False

    def love_goods(self, open_id, store_id):
        """
        推荐用户喜欢的商品
        :param open_id:
        :param store_id:
        :return:
        """

        class Goods(object):
            pass

        session = self.get_db_session(
            [{"db_table": "sdet_store_goods", "model": Goods}]
        )

        db = mongodb_util.db
        result = db.sdec_love_goods.find_one({"_id": open_id})
        is_membership = self.is_membership(open_id)
        goods_list = []
        filter = dict(
            sdef_goods_status=1,
            sdef_goods_is_delete=0,
            sdef_goods_store_id=store_id,
            sdef_goods_factory_uid=config.FACTORY_UID,
        )

        if result:
            id_list = result["id_list"]
        else:
            id_list = ["523948811961181", "523948088470142"]

        for id in id_list:
            filter["sdef_goods_id"] = id
            goods = (
                session.query(Goods)
                .filter(Goods.sdef_goods_count > 0)
                .filter_by(**filter)
                .first()
            )
            if not goods:
                continue

            goods_price = (
                goods.sdef_goods_member_price
                if is_membership
                else goods.sdef_goods_price
            )
            g = dict(
                id=id,
                goods_img=json.loads(goods.sdef_goods_thumbnail)[0],
                goods_name=goods.sdef_goods_name,
                goods_price="%0.2f" % goods_price,
            )
            goods_list.append(g)

        session.close()
        return goods_list

    def add_love_goods(self, open_id, goods_id):
        """
        根据用户浏览习惯添加用户喜欢的商品
        :param open_id:
        :param goods_id:
        :return:
        """
        try:
            db = mongodb_util.db
            result = db.sdec_love_goods.find_one({"_id": open_id})

            if result:
                id_list = result["id_list"]
                if goods_id in id_list:
                    return

                if len(id_list) == 4:
                    id_list.pop()

                id_list.insert(0, goods_id)
                db.sdec_love_goods.update_one(
                    {"_id": open_id}, {"$set": {"id_list": id_list}}
                )
            else:
                id_list = [goods_id]
                db.sdec_love_goods.insert({"_id": open_id, "id_list": id_list})
        except Exception as e:
            logger.error(e)

    def store_agency_id(self, open_id):
        class User(object):
            pass

        class Agency(object):
            pass

        class UserAgency(object):
            pass

        session = self.get_db_session(
            [
                {"db_table": "ebt_user", "model": User},
                {"db_table": "ebt_agency", "model": Agency},
                {"db_table": "ebt_user_agency", "model": UserAgency},
            ]
        )

        user_agency = (
            session.query(UserAgency).filter_by(ebf_ua_bind_user_id=open_id).first()
        )
        if not user_agency:
            logger.info(f"store_agency_id: 账号代理商关系表无记录: 普通用户 open_id={open_id}")
            session.close()
            return False, False

        agency = (
            session.query(Agency)
            .filter_by(ebf_agency_id=user_agency.ebf_agency_id)
            .first()
        )
        if not agency:
            logger.error(f"store_agency_id: 合伙人表无记录 open_id={open_id}")
            session.close()
            return False, False

        agency_id = agency.ebf_agency_id
        agency_type = agency.ebf_agency_type
        if (
            agency_type != AgencyType.STORE
            and agency_type != AgencyType.CLERK
            and agency_type != AgencyType.SUPPLIER
        ):
            session.close()
            return False, False

        if agency.ebf_agency_type == AgencyType.CLERK:
            parent_id = agency.ebf_agency_parent_id
            # 门店职员判定
            parent_agency = (
                session.query(Agency)
                .filter_by(ebf_agency_id=parent_id, ebf_agency_type=AgencyType.STORE)
                .first()
            )
            if not parent_agency:
                # 供应商职员判定
                parent_agency = (
                    session.query(Agency)
                    .filter_by(
                        ebf_agency_id=parent_id, ebf_agency_type=AgencyType.SUPPLIER
                    )
                    .first()
                )
                if not parent_agency:
                    logger.error(
                        f"store_agency_id: 店员所属商家不存在 open_id={open_id}, store_id={parent_id}"
                    )
                    session.close()
                    return False, False
            store_id = parent_id
        else:
            store_id = agency_id
        session.close()
        return agency_id, store_id

    def get_store_logo(self, agency_id, agency):
        class User(object):
            pass

        class Agency(object):
            pass

        session = self.get_db_session(
            [
                {"db_table": "ebt_user", "model": User},
                {"db_table": "ebt_agency", "model": Agency},
            ]
        )

        default_headurl = f"https://{DOMAIN}/static/img/content/logo-60x60.png"
        user_id1 = "P" + str(agency_id)
        user_id2 = (
            ("P" + agency.ebf_agency_contact) if agency.ebf_agency_contact else None
        )
        user_id3 = agency.ebf_agency_contact if agency.ebf_agency_contact else None
        id_list = [user_id1, user_id2, user_id3]
        user = (
            session.query(User)
            .filter(User.ebf_user_id.in_(id_list))
            .order_by(User.ebf_user_create_date.desc())
            .first()
        )
        if not user:
            if agency_id != 1:
                logger.error("用户表无记录： store_id=%s" % agency_id)

            session.close()
            return default_headurl

        headurl = user.ebf_user_headurl if user.ebf_user_headurl else default_headurl
        session.close()
        return headurl

    def is_first_buy(self, open_id):
        """判断用户是否第一次在商城购物"""

        class Order(object):
            pass

        session = self.get_db_session([{"db_table": "sdet_order", "model": Order}])

        filters = dict(sdef_order_device_id=open_id, sdef_order_type=1)
        count = (
            session.query(func.count(Order.sdef_order_id))
            .filter_by(**filters)
            .filter(
                or_(Order.sdef_order_pay_status == 1, Order.sdef_order_pay_status == 2)
            )
            .scalar()
        )
        if not count:
            count = 0

        session.close()
        return count


class MpBaseHandler(BaseHandler):
    pass


class PCBaseHandler(BaseHandler):
    def __init__(self, application, request, **kwargs):
        super().__init__(application, request, **kwargs)
        appid = config.WEB_APP_ID
        secret = config.WEB_APP_SECRET
        scope = "snsapi_login"
        state = uuid.uuid4().hex
        redirect_uri = "https://www.style999.com/api/user/wx_login"
        self.client = WeChatOAuth(appid, secret, redirect_uri, scope, state)

    def get_current_user(self):
        self.session = Session(self)
        return self.session.data

    def prepare(self):
        if self.request.headers.get("Content-Type", "").startswith("application/json"):
            self.json_args = json.loads(self.request.body.decode("utf-8"))
            self.get_parameter = self.json_args.get
        else:
            self.get_parameter = self.get_argument


def require_logined(fun):
    """对只有登录的用户才能发送的请求加此装饰器"""

    @functools.wraps(fun)
    def wrapper(request_handler, *args, **kwargs):
        # get_current_user方法返回一个非空字典，证明已经登录，保存了用户session数据
        if request_handler.get_current_user():
            fun(request_handler, *args, **kwargs)
        # 返回空字典，表示用户未登录
        else:
            request_handler.write(ret_msg(code=-1, error_message="用户未登录"))

    return wrapper


def get_user_no(open_id):
    """
    data=[{'ebf_user_no': 52837.0, 'ebf_user_id': 'o7qyl0i6R30kbFS7KPFVGwYb05pY'}]
    :param open_id:
    :return:
    """
    db = mongodb_util.db
    result = db.sdec_scan_no.find_one({"ebt_table": "scan_no"})
    now = datetime.datetime.now()
    if result:
        old_no = int(result["new_no"])
        no = old_no + 1
        update_date = now
        data = {"new_no": no, "old_no": old_no, "update_date": update_date}
        db.sdec_scan_no.update({"ebt_table": "scan_no"}, {"$set": data})
    else:
        session = get_db_session([])
        result_proxy = session.execute(
            """
            SELECT
                *
            FROM
                (
                    SELECT
                        (@i :=@i + 1) AS ebf_user_no,
                        ebf_user_id
                    FROM
                        ebt_user,
                        (SELECT @i := 0) AS x
                    ORDER BY
                        ebt_user.ebf_user_create_date ASC
                ) a
            WHERE
                a.ebf_user_id =:ebf_user_id
            """,
            {"ebf_user_id": open_id},
        )

        data = [dict(i) for i in result_proxy]
        no = int(data[0]["ebf_user_no"])
        content = dict(
            ebt_table="scan_no", new_no=no, old_no=no, create_date=now, update_date=now
        )
        db.sdec_scan_no.insert(content)
    logger.info("no=%s, update_date=%s, open_id=%s" % (no, now, open_id))
    return no
